package ch.comtools.ssh.packet;

import java.nio.ByteBuffer;

import ch.comtools.ssh.datatype.NameList;
import ch.comtools.ssh.message.Message;
import ch.comtools.ssh.util.TypeReader;

/**
 * <b>Key Exchange Initialization Packet.</b><br>
 * http://tools.ietf.org/html/draft-ietf-secsh-transport-24#section-7.1
 * <pre>
 * byte         SSH_MSG_KEXINIT
 * byte[16]     cookie (random bytes)
 * name-list    kex_algorithms
 * name-list    server_host_key_algorithms
 * name-list    encryption_algorithms_client_to_server
 * name-list    encryption_algorithms_server_to_client
 * name-list    mac_algorithms_client_to_server
 * name-list    mac_algorithms_server_to_client
 * name-list    compression_algorithms_client_to_server
 * name-list    compression_algorithms_server_to_client
 * name-list    languages_client_to_server
 * name-list    languages_server_to_client
 * boolean      first_kex_packet_follows
 * uint32       0 (reserved for future extension)
 * </pre>
 * @author Roger Dudler <roger.dudler@gmail.com>
 * @version $Id$
 */
public class SSHPacketKeyExchangeInitialization extends SSHPacketMessage {

	public byte[] cookie = new byte[16];
	public NameList kexAlgorithms;
	public NameList serverHostKeyAlgorithms;
	public NameList encryptionAlgorithmsClientToServer;
	public NameList encryptionAlgorithmsServerToClient;
	public NameList macAlgorithmsClientToServer;
	public NameList macAlgorithmsServerToClient;
	public NameList compressionAlgorithmsClientToServer;
	public NameList compressionAlgorithmsServerToClient;
	public NameList languagesClientToServer;
	public NameList languagesServerToClient;
	public boolean firstKexPacketFollows;
	public int reserved;
	
	/**
	 * Creates a new key exchange initialization packet.
	 */
	public SSHPacketKeyExchangeInitialization() {
		this.setType(Message.SSH_MSG_KEXINIT);
	}
	
	/**
	 * Parse a received key exchange initialization packet.
	 * @param buffer
	 */
	public SSHPacketKeyExchangeInitialization(ByteBuffer buffer) {
		this.read(buffer);
		ByteBuffer data = ByteBuffer.wrap(payload);
		
		// get message type
		byte type = data.get();
		System.out.println("Message Type: " + Message.getMessageName(type));
		
		// read data
		data.get(cookie, 0, 16);
		this.kexAlgorithms = TypeReader.readNameList(data);
		this.serverHostKeyAlgorithms = TypeReader.readNameList(data);
		this.encryptionAlgorithmsClientToServer = TypeReader.readNameList(data);
		this.encryptionAlgorithmsServerToClient = TypeReader.readNameList(data);
		this.macAlgorithmsClientToServer = TypeReader.readNameList(data);
		this.macAlgorithmsServerToClient = TypeReader.readNameList(data);
		this.compressionAlgorithmsClientToServer = TypeReader.readNameList(data);
		this.compressionAlgorithmsServerToClient = TypeReader.readNameList(data);
		this.languagesClientToServer = TypeReader.readNameList(data);
		this.languagesServerToClient = TypeReader.readNameList(data);
		this.firstKexPacketFollows = data.getInt() != 0;
		if (data.remaining() >= 4) {
			this.reserved = data.getInt();
		}
	}
	
	/**
	 * @see ch.comtools.ssh.packet.SSHPacket#getData()
	 */
	@Override
	public ByteBuffer getData() {
		ByteBuffer buffer = ByteBuffer.allocate(this.calculatePayloadLength());
		buffer.put((byte) Message.SSH_MSG_KEXINIT);
		buffer.put(this.cookie);
		buffer.put(this.kexAlgorithms.getBytes());
		buffer.put(this.serverHostKeyAlgorithms.getBytes());
		buffer.put(this.encryptionAlgorithmsClientToServer.getBytes());
		buffer.put(this.encryptionAlgorithmsServerToClient.getBytes());
		buffer.put(this.macAlgorithmsClientToServer.getBytes());
		buffer.put(this.macAlgorithmsServerToClient.getBytes());
		buffer.put(this.compressionAlgorithmsClientToServer.getBytes());
		buffer.put(this.compressionAlgorithmsServerToClient.getBytes());
		buffer.put(this.languagesClientToServer.getBytes());
		buffer.put(this.languagesServerToClient.getBytes());
		if (this.firstKexPacketFollows) {
			buffer.put((byte) 1);
		} else {
			buffer.put((byte) 0);
		}
		this.payload = buffer.array();
		buffer.flip();
		return super.getData();
	}
	
	/**
	 * @return
	 */
	private int calculatePayloadLength() {
		int length = 0;
		length += 1; // byte
		length += this.cookie.length;
		length += this.kexAlgorithms.getLength() + 4;
		length += this.serverHostKeyAlgorithms.getLength() + 4;
		length += this.encryptionAlgorithmsClientToServer.getLength() + 4;
		length += this.encryptionAlgorithmsServerToClient.getLength() + 4;
		length += this.macAlgorithmsClientToServer.getLength() + 4;
		length += this.macAlgorithmsServerToClient.getLength() + 4;
		length += this.compressionAlgorithmsClientToServer.getLength() + 4;
		length += this.compressionAlgorithmsServerToClient.getLength() + 4;
		length += this.languagesClientToServer.getLength() + 4;
		length += this.languagesServerToClient.getLength() + 4;
		length += 1; // boolean
		length += 4; // int
		return length;
	}
	
	/**
	 * @see ch.comtools.ssh.packet.SSHPacket#toString()
	 */
	@Override
	public String toString() {
		return super.toString() + "\n" +
			"       [Cookie (Length): " + this.cookie.length + "]\n" + 
			"       [Key Exchange Algorithms: " + this.kexAlgorithms + "]\n" + 
			"       [Server Host Key Algorithms: " + this.serverHostKeyAlgorithms + "]\n" +
			"       [Encryption Algorithms (Client -> Server): " + this.encryptionAlgorithmsClientToServer + "]\n" +
			"       [Encryption Algorithms (Server -> Client): " + this.encryptionAlgorithmsServerToClient + "]\n" +
			"       [MAC Algorithms (Client -> Server): " + this.macAlgorithmsClientToServer + "]\n" +
			"       [MAC Algorithms (Server -> Client): " + this.macAlgorithmsServerToClient + "]\n" +
			"       [Compression Algorithms (Client -> Server): " + this.compressionAlgorithmsClientToServer + "]\n" +
			"       [Compression Algorithms (Server -> Client): " + this.compressionAlgorithmsServerToClient + "]\n" +
			"       [Languages (Client -> Server): " + this.languagesClientToServer + "]\n" +
			"       [Languages (Server -> Client): " + this.languagesServerToClient + "]\n" +
			"       [First Key Exchange Packet follows?: " + this.firstKexPacketFollows + "]\n" +
			"       [Reserved: " + this.reserved + "]" +
		"";
	}

}
